package redis.client.nedis;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import io.netty.util.CharsetUtil;
import redis.client.nedis.reply.ErrorReply;
import redis.client.nedis.reply.IReply;
import redis.client.nedis.reply.IntegerReply;
import redis.client.nedis.reply.StatusReply;

import java.io.IOException;
import java.util.List;

/**
 * @author gaodong
 * @version 1.0
 * @date 2020/11/18 9:58
 */
public class RedisReplyDecoder extends ReplayingDecoder<Void> {

    public static final char CR = '\r';
    public static final char LF = '\n';
    public static final char ZERO = '0';

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        long s = System.nanoTime();
        out.add(receive(in));
        long e = System.nanoTime();
        //System.out.println("decode=" + (e - s) / 1e6 + "ms");
    }

    public IReply receive(final ByteBuf is) throws IOException {
        return readReply(is);
    }

    public IReply readReply(ByteBuf is) throws IOException {
        int code = is.readByte();
        switch (code) {
            case StatusReply.MARKER: {
                String status = is.readBytes(is.bytesBefore((byte) '\r')).toString(CharsetUtil.UTF_8);
                is.skipBytes(2);
                return new StatusReply(status);
            }
            case ErrorReply.MARKER: {
                String error = is.readBytes(is.bytesBefore((byte) '\r')).toString(CharsetUtil.UTF_8);
                is.skipBytes(2);
                return new ErrorReply(error);
            }
            case IntegerReply.MARKER: {
                return new IntegerReply(readLong(is));
            }
            /*case BulkReply.MARKER: {
                return new BulkReply(readBytes(is));
            }
            case MultiBulkReply.MARKER: {
                if (reply == null) {
                    return decodeMultiBulkReply(is);
                } else {
                    return new RedisReplyDecoder(false).decodeMultiBulkReply(is);
                }
            }*/
            default: {
                throw new IOException("Unexpected character in stream: " + code);
            }
        }
    }

    public ByteBuf readBytes(ByteBuf is) throws IOException {
        long l = readLong(is);
        if (l > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("Java only supports arrays up to " + Integer.MAX_VALUE + " in size");
        }
        int size = (int) l;
        if (size == -1) {
            return null;
        }
        ByteBuf buffer = is.readSlice(size);
        int cr = is.readByte();
        int lf = is.readByte();
        if (cr != CR || lf != LF) {
            throw new IOException("Improper line ending: " + cr + ", " + lf);
        }
        return buffer;
    }

    public static long readLong(ByteBuf is) throws IOException {
        long size = 0;
        int sign = 1;
        int read = is.readByte();
        if (read == '-') {
            read = is.readByte();
            sign = -1;
        }
        do {
            if (read == CR) {
                if (is.readByte() == LF) {
                    break;
                }
            }
            int value = read - ZERO;
            if (value >= 0 && value < 10) {
                size *= 10;
                size += value;
            } else {
                throw new IOException("Invalid character in integer");
            }
            read = is.readByte();
        } while (true);
        return size * sign;
    }
}
